// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package color

// RGBToYCbCr将RGB三元组转换为Y'CbCr三元组。
func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
	// JFIF规范说明：
	// Y'=0.2990*R+0.5870*G+0.1140*B 
	// Cb=-0.1687*R-0.3313*G+0.5000*B+128 
	// Cr=0.5000*R-0.4187*G-0.0813*B+128 

	r1 := int32(r)
	g1 := int32(g)
	b1 := int32(b)

	yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16

	// 下面的位旋转相当于
	// 
	// cb:=（-11056*r1-21712*g1+32768*b1+257<15）>>16 
	// 如果cb<0{
	// cb 0 
	// /}如果cb而且速度更快。
	// 注意，返回
	// 语句中的uint8类型转换将^int32（0）转换为0xff。
	// 下面计算cr的代码使用了类似的模式。注意-11056-21712+32768等于0。注意32768-27440-5328等于0。
	cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
	if uint32(cb)&0xff000000 == 0 {
		cb >>= 16
	} else {
		cb = ^(cb >> 31)
	}

	cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
	if uint32(cr)&0xff000000 == 0 {
		cr >>= 16
	} else {
		cr = ^(cr >> 31)
	}

	return uint8(yy), uint8(cb), uint8(cr)
}

// YCBRTORGB将Y'CbCr三元组转换为RGB三元组。
func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
	// JFIF规范规定：
	// R=Y'+1.40200*（Cr-128）
	// G=Y'-0.34414*（Cb-128）-0.71414*（Cr-128）
	// B=Y'+1.77200*（Cb-128）
	// https:
	// 这些公式使用非整数乘法因子。计算时，整数数学通常比浮点数学快。我们将所有这些因子乘以1<<16，并四舍五入到最接近的整数：
	// 91881=roundtonearestineger（1.40200*65536）。
	// 22554=圆度内测图（0.34414*65536）。
	// 46802=roundtonearestineger（0.71414*65536）。
	// 116130=圆通内测图（1.77200*65536）。
	// 
	// 在[0,1<<16-1]范围内添加舍入调整，然后将
	// 右移16，得到原始公式的整数数学版本。
	// R=（65536*Y'+91881*（Cr-128）+调整）>>16 
	// G=（65536*Y'-22554*（Cb-128）-46802*（Cr-128）+调整）>>16 
	// 当除以65536（右移16）时，四舍五入到最近。
	// 类似地，0的恒定舍入调整意味着向下舍入。
	// 
	// 定义YY1=65536*Y'+调整简化了公式，
	// 需要更少的CPU操作：
	// R=（YY1+91881*（Cr-128））>>16 
	// G=（YY1-22554*（Cb-128）-46802*（Cr-128））>>16 
	// 输入（y、cb、cr）为8位颜色，范围为[0x00、0xff]。在这个
	// 函数中，输出也是8位颜色，但在相关的YCbCr中。RGBA 
	// 方法，如下，输出为16位颜色，范围为[0x0000，0xffff]。
	// 输出16位颜色只需将“R=
	// 等>>16”等式中的16改为8即可，对于G和B也是如此。
	// 
	// 如上所述，1<<15是一个自然的
	// 选择，但还有一个附加约束：如果c0:=YCbCr{Y:Y，Cb:
	// 0x80，Cr:0x80}和c1:=Gray{Y:Y}，那么c0。RGBA（）应该等于
	// c1。RGBA（）。具体来说，如果y==0，则“R=etc>>8”应产生
	// 0x0000，如果y==0xff，则“R=etc>>8”应产生0xffff。如果我们
	// 使用1<<15的常数舍入调整，那么它将分别产生0x0080 
	// 和0xff80。
	// 
	// 请注意，当cb==0x80和cr==0x80时，公式折叠为：
	// R=YY1>>n 
	// G=YY1>>n 
	// B=YY1>>n 
	// 其中n为16表示该函数（8位颜色输出），8表示
	// 
	// 解决方案是使舍入调整为非常数，并等于
	// 到257*Y'，其范围超过[0,1<<16-1]，正如Y'范围超过[0,255]。
	// YY1则被定义为：
	// YY1=65536*Y'+257*Y'
	// 或等效：
	// YY1=Y'*0x10101 
	yy1 := int32(y) * 0x10101
	cb1 := int32(cb) - 128
	cr1 := int32(cr) - 128

	// 下面的位旋转相当于
	// 
	// r=^int32（0）
	// }
	// 
	// 但使用的分支更少，速度更快。
	// 注意，返回
	// 语句中的uint8类型转换将^int32（0）转换为0xff。
	// 下面计算g和b的代码使用了类似的模式。
	r := yy1 + 91881*cr1
	if uint32(r)&0xff000000 == 0 {
		r >>= 16
	} else {
		r = ^(r >> 31)
	}

	g := yy1 - 22554*cb1 - 46802*cr1
	if uint32(g)&0xff000000 == 0 {
		g >>= 16
	} else {
		g = ^(g >> 31)
	}

	b := yy1 + 116130*cb1
	if uint32(b)&0xff000000 == 0 {
		b >>= 16
	} else {
		b = ^(b >> 31)
	}

	return uint8(r), uint8(g), uint8(b)
}

// YCbCr代表完全不透明的24位Y'CbCr颜色，每个8位代表
// 一个亮度和两个色度分量。
// 
// JPEG、VP8、MPEG系列和其他编解码器使用这种颜色模型。这样的
// 编解码器通常交替使用YUV和Y'CbCr这两个术语，但严格来说，YUV这一术语仅适用于模拟视频信号，而Y'（luma）
// 是应用伽马校正后的Y（亮度）。
// 
// RGB和Y'CbCr之间的转换是有损的，并且有多个略微不同的
// 用于在两者之间转换的不同公式。此软件包遵循
// JFIF规范https:
type YCbCr struct {
	Y, Cb, Cr uint8
}

func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) {
	// 此代码是上述YCbCrToRGB函数的副本，只是它返回[0，0xffff]范围内的值，而不是[0，0xff]。这样做与通过首先转换为RGBA使YCbCr满足颜色
	// 接口之间存在细微差别。后者通过每个通道往返8位来丢失一些
	// 信息。
	// 
	// 例如，这个代码：
	// 常数y，cb，cr=0x7f，0x7f，0x7f 
	// r，g，b:=color。YCbCrToRGB（y，cb，cr）
	// r0，g0，b0，:=颜色。YCbCr{y，cb，cr}。RGBA（）
	// r1，g1，b1，:=颜色。RGBA{r，g，b，0xff}。RGBA（）
	// fmt。Printf（“0x%04x 0x%04x 0x%04x\n”，r0，g0，b0）
	// fmt。Printf（“0x%04x 0x%04x 0x%04x\n”，r1，g1，b1）
	// prints:
	// 0x7e18 0x808d 0x7db9 
	// 0x7e7e 0x8080 0x7d7d 

	yy1 := int32(c.Y) * 0x10101
	cb1 := int32(c.Cb) - 128
	cr1 := int32(c.Cr) - 128

	// 下面的位旋转相当于
	// r=0xffff 
	// }
	// 
	// 但使用的分支更少，速度更快。
	// 下面计算g和b的代码使用了类似的模式。
	r := yy1 + 91881*cr1
	if uint32(r)&0xff000000 == 0 {
		r >>= 8
	} else {
		r = ^(r >> 31) & 0xffff
	}

	g := yy1 - 22554*cb1 - 46802*cr1
	if uint32(g)&0xff000000 == 0 {
		g >>= 8
	} else {
		g = ^(g >> 31) & 0xffff
	}

	b := yy1 + 116130*cb1
	if uint32(b)&0xff000000 == 0 {
		b >>= 8
	} else {
		b = ^(b >> 31) & 0xffff
	}

	return uint32(r), uint32(g), uint32(b), 0xffff
}

// YCbCrModel是Y'CbCr颜色的模型。
var YCbCrModel Model = ModelFunc(yCbCrModel)

func yCbCrModel(c Color) Color {
	if _, ok := c.(YCbCr); ok {
		return c
	}
	r, g, b, _ := c.RGBA()
	y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
	return YCbCr{y, u, v}
}

// NYCbCrA代表一个非alpha预乘Y'CbCr-with-alpha颜色，具有
// 8位，分别代表一个亮度、两个色度和一个alpha分量。
type NYCbCrA struct {
	YCbCr
	A uint8
}

func (c NYCbCrA) RGBA() (uint32, uint32, uint32, uint32) {
	// 此方法的第一部分与YCbCr相同。RGBA。
	yy1 := int32(c.Y) * 0x10101
	cb1 := int32(c.Cb) - 128
	cr1 := int32(c.Cr) - 128

	// 如果r<0{
	// r=0 
	// r>0xff{
	// r=0xffff-cdefg
	// 。
	// 下面计算g和b的代码使用了类似的模式。
	r := yy1 + 91881*cr1
	if uint32(r)&0xff000000 == 0 {
		r >>= 8
	} else {
		r = ^(r >> 31) & 0xffff
	}

	g := yy1 - 22554*cb1 - 46802*cr1
	if uint32(g)&0xff000000 == 0 {
		g >>= 8
	} else {
		g = ^(g >> 31) & 0xffff
	}

	b := yy1 + 116130*cb1
	if uint32(b)&0xff000000 == 0 {
		b >>= 8
	} else {
		b = ^(b >> 31) & 0xffff
	}

	// 此方法的第二部分应用alpha。
	a := uint32(c.A) * 0x101
	return uint32(r) * a / 0xffff, uint32(g) * a / 0xffff, uint32(b) * a / 0xffff, a
}

// NYCbCrAModel是非alpha预乘Y'CbCr-with-alpha 
// 颜色的模型。
var NYCbCrAModel Model = ModelFunc(nYCbCrAModel)

func nYCbCrAModel(c Color) Color {
	switch c := c.(type) {
	case NYCbCrA:
		return c
	case YCbCr:
		return NYCbCrA{c, 0xff}
	}
	r, g, b, a := c.RGBA()

	// 从alpha预乘转换为非alpha预乘。
	if a != 0 {
		r = (r * 0xffff) / a
		g = (g * 0xffff) / a
		b = (b * 0xffff) / a
	}

	y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
	return NYCbCrA{YCbCr{Y: y, Cb: u, Cr: v}, uint8(a >> 8)}
}

// RGBToCMYK将RGB三元组转换为CMYK四元组。
func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) {
	rr := uint32(r)
	gg := uint32(g)
	bb := uint32(b)
	w := rr
	if w < gg {
		w = gg
	}
	if w < bb {
		w = bb
	}
	if w == 0 {
		return 0, 0, 0, 0xff
	}
	c := (w - rr) * 0xff / w
	m := (w - gg) * 0xff / w
	y := (w - bb) * 0xff / w
	return uint8(c), uint8(m), uint8(y), uint8(0xff - w)
}

// CMYKToRGB将CMYK四元组转换为RGB三元组。
func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) {
	w := 0xffff - uint32(k)*0x101
	r := (0xffff - uint32(c)*0x101) * w / 0xffff
	g := (0xffff - uint32(m)*0x101) * w / 0xffff
	b := (0xffff - uint32(y)*0x101) * w / 0xffff
	return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)
}

// CMYK代表完全不透明的CMYK颜色，青色、
// 品红、黄色和黑色各有8位。
// 
// 它与任何特定的颜色配置文件都没有关联。
type CMYK struct {
	C, M, Y, K uint8
}

func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) {
	// 此代码是上述CMYKToRGB函数的副本，只是它返回[0，0xffff]范围内的值，而不是[0，0xff]。

	w := 0xffff - uint32(c.K)*0x101
	r := (0xffff - uint32(c.C)*0x101) * w / 0xffff
	g := (0xffff - uint32(c.M)*0x101) * w / 0xffff
	b := (0xffff - uint32(c.Y)*0x101) * w / 0xffff
	return r, g, b, 0xffff
}

// CMYK模型是CMYK颜色的模型。
var CMYKModel Model = ModelFunc(cmykModel)

func cmykModel(c Color) Color {
	if _, ok := c.(CMYK); ok {
		return c
	}
	r, g, b, _ := c.RGBA()
	cc, mm, yy, kk := RGBToCMYK(uint8(r>>8), uint8(g>>8), uint8(b>>8))
	return CMYK{cc, mm, yy, kk}
}
